#!/usr/local/bin/ruby -w

# fairy_chess
#
#  Created by James Edward Gray II on 2005-06-20.
#  Copyright 2005 Gray Productions. All rights reserved.

require "chess"

#
# The container for the behavior of a chess fairy.  Fairies are simply treated
# as both a Queen and a Knight.
# 
class Fairy < Chess::Queen
	#
	# Returns all the capturing moves for a Fairy on the provided _board_
	# at the provided _square_ of the provided _color_.
	# 
	def self.captures( board, square, color )
		captures =  Chess::Queen.captures(board, square, color)
		captures += Chess::Knight.captures(board, square, color)
		captures.sort
	end

	#
	# Returns all the non-capturing moves for a Fairy on the provided
	# _board_ at the provided _square_ of the provided _color_.
	# 
	def self.moves( board, square, color )
		moves =  Chess::Queen.moves(board, square, color)
		moves += Chess::Knight.moves(board, square, color)
		moves.sort
	end
end

# Make the Chess::King aware of the Fairy.
class FairyAwareKing < Chess::King
	# Enhance in_check? to spot special Fairy moves.
	def self.in_check?( bd, sq, col )
		return true if Chess::Knight.captures(bd, sq, col).any? do |name|
			bd[name].is_a?(Fairy)
		end

		Chess::King.in_check?( bd, sq, col )
	end

	# Make this piece show up as a normal King.
	def to_s(  )
		if @color == :white then "K" else "k" end
	end
end

# An enhanced chess board for playing Fairy Chess.
class FairyChess < Chess::Board
	# Setup a normal board, then replace the queens with fairies.
	def setup(  )
		super
		
		@squares["d1"] = Fairy.new(self, "d1", :white)
		@squares["d8"] = Fairy.new(self, "d8", :black)
		@squares["e1"] = FairyAwareKing.new(self, "e1", :white)
		@squares["e8"] = FairyAwareKing.new(self, "e8", :black)
	end
end

board = FairyChess.new

puts
puts "Welcome to Ruby Quiz Fairy Chess."

# player move loop
loop do
	# show board
	puts
	puts board
	puts

	# watch for end conditions
	if board.in_checkmate?
		puts "Checkmate!  " +
		     "It's #{board.turn == :white ? 'Black' : 'White'}'s game."
		puts
		break
	elsif board.in_stalemate?
		puts "Stalemate."
		puts
		break
	elsif board.in_check?
		puts "Check."
	end

	# move input loop
	move = nil
	loop do
		print "#{board.turn.to_s.capitalize}'s Move (from to):  "
		move = $stdin.gets.chomp
		
		# validate move
		moves = board.moves
		if move !~ /^\s*([a-h][1-8])\s*([a-h][1-8])\s*$/
			puts "Invalid move format.  Use from to.  (Example:  e2 e4.)"
		elsif board[$1].nil?
			puts "No piece on that square."
		elsif board[$1].color != board.turn
			puts "That's not your piece to move."
		elsif board.in_check? and ( (m = moves.assoc($1)).nil? or
			                        not m.last.include?($2) )
			puts "You must move out of check."
		elsif not (board[$1].captures + board[$1].moves).include?($2)
			puts "That piece can't move to that square."
		elsif ((m = moves.assoc($1)).nil? or not m.last.include?($2))
			puts "You can't move into check."
		else
			break
		end
	end
	
	# make move, with promotion if needed
	if board[$1].is_a?(Chess::Pawn) and $2[1, 1] == "8"
		from, to = $1, $2

		print "Promote to (k, b, r, or q)?  "
		promote = $stdin.gets.chomp
		
		case promote.downcase[0, 1]
		when "k"
			board.move($1, $2, Chess::Knight)
		when "b"
			board.move($1, $2, Chess::Bishop)
		when "r"
			board.move($1, $2, Chess::Rook)
		else
			board.move($1, $2, Chess::Queen)
		end
	else
		board.move($1, $2)
	end
end
